using System;
using System.Collections.ObjectModel;
using DslCrossModelFramework.NameResolution;
using Microsoft.VisualStudio.Modeling;

namespace DslCrossModelFramework.ModelTraversal {

    public static class ModelPathHelper {

        /// <summary>
        /// Gets the link from parent to child.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="child">The child.</param>
        /// <returns></returns>
        public static ModelPath GetLinkFromParentToChild(ModelElement parent, ModelElement child) {
            CrossModelManager manager = new CrossModelManager(parent.Store);
            ModelPath modelPath = new ModelPath();
            modelPath.AddLast(new ModelElementPathSegment(parent, manager));
            ElementLink link = null;
            bool reverse = false;
            foreach (DomainRoleInfo domainRoleInfo in parent.GetDomainClass().AllDomainRolesPlayed) {
                if (domainRoleInfo.OppositeDomainRole.RolePlayer == child.GetDomainClass() || child.GetDomainClass().IsDerivedFrom(domainRoleInfo.OppositeDomainRole.RolePlayer)) {
                    foreach (ElementLink currentLink in domainRoleInfo.GetElementLinks(parent)) {
                        if (currentLink.LinkedElements.Contains(parent) && currentLink.LinkedElements.Contains(child)) {
                            link = currentLink;
                            break;
                        }
                    }
                }
                reverse = !domainRoleInfo.IsSource;

                if (link != null) {
                    break;
                }
            }

            if(link == null ) {
                throw new NoDirectElementLink(parent, child);
            }
            modelPath.AddLast(new ModelElementLinkPathSegment(link, reverse ? ModelPathLinkDirection.TargetToSource : ModelPathLinkDirection.SourceToTarget, manager));
            modelPath.AddLast(new ModelElementPathSegment(child, manager));

            return modelPath;
        }
    }

    /// <summary>
    /// Exception raised when there is no direct can be found between two domain classes
    /// </summary>
    public class NoDirectElementLink : Exception {
        public NoDirectElementLink(ModelElement parent, ModelElement child)
            : base(string.Format("Cannot find a direct link between element '{0}' and element '{1}'. You may have to create a custom Relationship Definition and override the GetChildPaths method.", NameResolutionManager.Current.GetElementName(parent), NameResolutionManager.Current.GetElementName(child))) {

        }
    }
}